package com.resLoader
{
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	import flash.utils.Dictionary;
	
	import com.utils.Print;

	/**
	 * 批量下载
	 * @author hyy
	 *
	 */
	public class Resources
	{
		/**
		 * 记录已经加载过的地址
		 */
		protected var loadFinishArray : Array = [];

		/**
		 * 记录已经放入加载器的地址
		 */
		protected var loadUrlArray : Array = [];

		/**
		 * 记录当前正在下载的加载器
		 */
		protected var resources : Array = [];

		/**
		 * 批量加载完成数量
		 */
		protected var finishCount : int;

		/**
		 * 锁定批量加载
		 */
		protected var lockBathLoad : Boolean;

		/**
		 * 加载进度条
		 */
		protected var progressInfo : ProgressInfo;

		/**
		 * 加载完毕返回函数
		 */
		protected var returnFun : Function;

		/**
		 * 进度回调
		 */
		protected var progressFun : Function;

		/**
		 * 下载后的数据
		 */
		protected var lib : Dictionary;

		/**
		 * 单例
		 */
		protected static var instance : Resources;

		public function Resources()
		{
			lib = new Dictionary();
			progressInfo = new ProgressInfo();
		}

		public static function getInstance() : Resources
		{
			if (instance == null)
			{
				instance = new Resources();
			}
			return instance;
		}

		/**
		 * 只用来批量加载
		 * @param url     				目标资源地址
		 * @param type					资源类型,如swf,txt,sound
		 * @param priority				加载优先级，数字越大优先级越高
		 * @param version				资源版本号
		 *
		 */
		public function addData(url : String, type : String, priority : int = 0) : void
		{
			var resData : ResData = new ResData(url, type, priority);
			addResData(resData);
		}

		/**
		 * 只用来批量加载
		 * 记录需要加载的地址
		 * @param data
		 *
		 */

		public function addResData(resData : ResData) : void
		{
			//停止已经正在加载
			stopPreloadHandler(resData.url);

			if (loadUrlArray.indexOf(resData.url) != -1 || isFinishLoad(resData.url) || lockBathLoad)
			{
				//Print.error("不需要再加载:", resData.url);
				return;
			}

			loadUrlArray.push(resData.url);

			var res : Resource = createResource(resData, onSyncCompleteHandler, onSyncErrorHandler, onProgressEvent);

			if (res)
			{
				resources.push(res);
			}
		}

		/**
		 * 开始批量,并发加载
		 * 使用时需要注意，如果有多重嵌套使用，需要手动完成reset();否则会出现加载不了的现象
		 */
		public function startLoad(returnFun : Function, progressFun : Function = null, context : LoaderContext = null) : void
		{
			if (lockBathLoad)
			{
				//Print.error("警告：当前正在进行批量加载!");
				return;
			}

			finishCount = 0;
			lockBathLoad = true;
			this.returnFun = returnFun;
			this.progressFun = progressFun;

			if (resources.length == 0)
			{
				checkSyncLoad();
				return;
			}

			if (context == null)
			{
				context = new LoaderContext();
				context.applicationDomain = ApplicationDomain.currentDomain;
			}

			for each (var resource : Resource in resources)
			{
				resource.load(context);
					//Print.print("批量下载:", resource.url);
			}
		}

		/**
		 * 根据data,创建所对应的资源下载器
		 * @param resData
		 * @return
		 *
		 */
		public function createResource(resData : ResData, onComplete : Function, onError : Function = null, onProgress : Function = null) : Resource
		{
			var res : Resource;
			var url : String = resData.url;

			switch (resData.type)
			{
				case ResData.IMAGE:
					res = new IMGResource(url, onComplete, onError, onProgress);
					break;
				case ResData.MP3:
					res = new MP3Resource(url, onComplete, onError, onProgress);
					break;
				case ResData.SWF:
					res = new SWFResource(url, onComplete, onError, onProgress);
					break;
				case ResData.TEXT:
					res = new TEXTResource(url, onComplete, onError, onProgress);
					break;
				case ResData.XML:
					res = new XMLResource(url, onComplete, onError, onProgress);
					break;
				default:
					res = new TEXTResource(url, onComplete, onError, onProgress);
					break;
			}
			res.priority = resData.priority;
			res.resData = resData;
			return res;
		}

		/**
		 * 并发加载单个完成加载
		 *
		 */
		protected function onSyncCompleteHandler(res : Resource) : void
		{
			//Print.warning("完成批量下载:", res.url);

			saveLib(res);
			finishCount++;

			saveLoadUrl(res.url);
			checkSyncLoad();
		}

		/**
		 * 保存已经加载完成的地址
		 * @param resUrl
		 *
		 */
		protected function saveLoadUrl(resUrl : String) : Boolean
		{
			if (loadFinishArray.indexOf(resUrl) == -1)
			{
				loadFinishArray.push(resUrl);
				return true;
			}
			return false;
		}

		/**
		 * 检测是否已经加载过了该地址
		 * @param resUrl
		 * @return
		 *
		 */
		public function isFinishLoad(resUrl : String) : Boolean
		{
			return loadFinishArray.indexOf(resUrl) != -1;
		}

		/**
		 * 并发加载错误
		 * @param res
		 *
		 */
		protected function onSyncErrorHandler(res : Resource) : void
		{
			finishCount++;
			checkSyncLoad();

			//加载完毕后检测是否有东西需要后台加载
			if (preLoadArray.length > 0)
			{
				Print.error("无需要后台加载资源:", preLoadArray.length);
			}
			else
			{
				Print.error("后台加载资源数量:", preLoadArray.length);
			}
			trace("加载错误:", res.url);
			Print.error(res.url)
		}

		/**
		 * 检测并发加载是否完毕
		 *
		 */
		protected function checkSyncLoad() : void
		{
			if (finishCount == resources.length)
			{
				if (returnFun != null)
				{
					Print.print("剩余后台下载:", preLoadArray.length);
					returnFun(this);
					curr_load = 0;
					loadNext();
				}
			}
		}

		/**
		 * 加载进度
		 * @param evt
		 *
		 */
		protected function onProgressEvent(info : Resource) : void
		{
			progressInfo.bytesLoaded = 0;
			progressInfo.bytesTotal = 0;

			for each (var resource : Resource in resources)
			{
				progressInfo.bytesTotal += resource.bytesTotal;
				progressInfo.bytesLoaded += resource.bytesLoaded;
			}

			if (progressFun != null)
			{
				progressFun(progressInfo);
			}
		}

		public function setProgressFun(value : Function) : void
		{
			this.progressFun = value;
		}

		public function getProgress() : ProgressInfo
		{
			return progressInfo;
		}

		/**
		 * 保存数据
		 * 只会保存文本,和xml
		 * @param resource
		 *
		 */
		protected function saveLib(resource : Resource) : *
		{
			var url : String = parseResUrl(resource.url);
			var data : *;

			if (lib[url])
			{
				return lib[url];
			}

			if (resource is IMGResource)
			{
				data = IMGResource(resource).bitmapData;
			}
			else if (resource is MP3Resource)
			{
				data = MP3Resource(resource).sound;
			}
			else if (resource is TEXTResource)
			{
				data = TEXTResource(resource).byteArray;
			}
			else if (resource is XMLResource)
			{
				data = XMLResource(resource).xml;
			}
			this.lib[url] = data;
			return data;
		}

		/**
		 * 必须根据完全的加载地址来查询
		 * ../res/config.xml
		 * @param url
		 * @return
		 *
		 */
		public function getData(url : String) : *
		{
			if (lib[url])
			{
				return lib[url]
			}
			return null;
		}

		/**
		 * 只根据文件名查找
		 * 如果有重复文件名
		 * 则查找会不准确
		 * @param name
		 * @return
		 *
		 */
		public function getDataByName(name : String) : *
		{
			var keyName : String;

			for (var key : String in lib)
			{
				keyName = key.split("/").pop();

				if (keyName.indexOf(name) == 0)
				{
					return lib[key];
				}
			}
			return null;
		}

		/**
		 * 获取加载类中资源
		 * @param resName
		 * @return
		 *
		 */
		public function getClass(resName : String) : Class
		{
			if (resName == "")
				return null;

			if (ApplicationDomain.currentDomain.hasDefinition(resName))
			{
				return ApplicationDomain.currentDomain.getDefinition(resName) as Class;
			}
			return null;
		}

		/**
		 * 解析地址,比如..res/role/角色.xml?v='5'  res/role/变成角色.xml
		 * @param url
		 * @return
		 *
		 */
		protected function parseResUrl(url : String) : String
		{
			url = url.split("?v=")[0];
			return url;
		}

		/**
		 *
		 * 重置数据
		 * 切勿正加载资源时调用该方法
		 * 否则会导致不可知错误
		 *
		 */
		public function reset() : void
		{
			for each (var res : Resource in resources)
			{
				res.destory();
			}
			this.finishCount = 0;
			returnFun = null;
			loadUrlArray.length = 0;
			resources.length = 0;
			lockBathLoad = false;
			progressFun = null;
		}

		/**
		 * 后台加载,如果正在批量加载,则会停止当前加载
		 * 先进先出
		 * @param url
		 * @param type
		 * @param priority
		 * @param version
		 * @param onComplete
		 *
		 */
		private var preLoadArray : Array = [];

		private var curr_load : int = 0;

		private const MAX_PRELOAD : int = 2;

		public function preLoad(url : String, type : String, priority : int = 0, onComplete : Function = null) : void
		{
			var resData : ResData = new ResData(url, type, priority, onComplete);
			preLoadResData(resData);
		}

		public function preLoadResData(resData : ResData) : void
		{
			//已经加载完成的，直接实行返回函数
			if (isFinishLoad(resData.url))
			{
				resData.excute(getData(resData.url));
				return;
			}

			var tmpRes : Resource;

			//查看已经加载的地址是否有重复的，如果有则在原来返回函数中加入现在的函数
			for each (tmpRes in preLoadArray)
			{
				if (tmpRes.url == resData.url)
				{
					if (tmpRes.resData)
					{
						tmpRes.resData.addReturnFun(resData.onCompletes);
					}
					return;
				}
			}

			var res : Resource = createResource(resData, onPreloadCompleteHandler, onPreloadErrorHandler);

			if (res)
			{
				preLoadArray.push(res);
				preLoadArray.sortOn("priority", Array.NUMERIC | Array.DESCENDING);
				loadNext();
			}
		}

		/**
		 * 后台加载完成
		 * @param res
		 *
		 */
		private function onPreloadCompleteHandler(res : Resource) : void
		{
			var index : int = preLoadArray.indexOf(res);

			if (index != -1)
			{
				preLoadArray.splice(index, 1);
				//保存加载数据
				var data : * = saveLib(res);
				//保存加载地址
				var tag : Boolean = saveLoadUrl(res.url);

				if (tag)
				{
					//Print.warning("完成后台下载:", res.url, preLoadArray.length);
				}

				if (res.resData)
				{
					res.resData.excute(data);
				}
				res.destory();
				curr_load--;
				loadNext();
			}
		}

		/**
		 * 后台加载失败
		 * @param res
		 *
		 */
		private function onPreloadErrorHandler(res : Resource) : void
		{
			Print.warning("后台加载错误:", res.url);
			//onPreloadCompleteHandler(res);
		}

		/**
		 * 后台加载下一个
		 *
		 */
		private function loadNext() : void
		{
			if (lockBathLoad)
			{
				return;
			}

			if (curr_load < MAX_PRELOAD && preLoadArray.length > 0 && preLoadArray.length > curr_load)
			{
				var res : Resource = getNeedLoadResource();

				if (res)
				{
					curr_load++;

					//已经加载过了
					if (isFinishLoad(res.url))
					{
						onPreloadCompleteHandler(res)
					}
					else
					{
						res.load();
							//Print.print("后台下载:", res.url, preLoadArray.length);
					}

					loadNext();
				}
			}
		}

		private function getNeedLoadResource() : Resource
		{
			var len : int = preLoadArray.length;
			var res : Resource

			for (var i : int = 0; i < len; i++)
			{
				res = preLoadArray[i];

				if (res.isLoading)
				{
					continue;
				}
				return res;
			}
			return null;
		}

		/**
		 * 检测后台加载中是否有正在加载的地址
		 * 有的话责停止加载
		 * @param url
		 *
		 */
		private function stopPreloadHandler(url : String) : void
		{
			var len : int = preLoadArray.length;
			var res : Resource;

			for (var i : int = len - 1; i >= 0; i--)
			{
				res = preLoadArray[i];

				if (res && res.url == url)
				{
					if (res.isLoading)
					{
						res.stop();
					}
					res.isLoading = false;
					preLoadArray.splice(i, 1);
				}
			}
		}
	}
}